//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package roadmap_dao

import (
	"context"
	"fmt"

	"atomgit.com/openlinksaas-org/api-server/dao/db_ns"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/roadmap_state_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type NodeTopicStateDbItem struct {
	State uint32 `bson:"state"`
	Title string `bson:"string"`
}

type NodeTodoStateItemDbItem struct {
	TodoId string `bson:"todoId"`
	Title  string `bson:"title"`
}

type NodeTodoListStateDbItem struct {
	DoneList []NodeTodoStateItemDbItem `bson:"doneList"`
}

type BasicStateInfoDbItem struct {
	TopicState    *NodeTopicStateDbItem    `bson:"topicState"`
	TodoListState *NodeTodoListStateDbItem `bson:"todoListState"`
}

type StateInfoDbItem struct {
	ItemId    string               `bson:"_id"` //自动生成
	RoadmapId string               `bson:"roadmapId"`
	NodeId    string               `bson:"nodeId"`
	UserId    string               `bson:"userId"`
	TimeStamp int64                `bson:"timeStamp"`
	BasicInfo BasicStateInfoDbItem `bson:"basicInfo"`
}

func (item *StateInfoDbItem) ToStateInfo() *roadmap_state_api.StateInfo {
	retItem := &roadmap_state_api.StateInfo{
		RoadmapId: item.RoadmapId,
		NodeId:    item.NodeId,
		TimeStamp: item.TimeStamp,
		BasicInfo: &roadmap_state_api.BasicStateInfo{},
	}
	if item.BasicInfo.TopicState != nil {
		retItem.BasicInfo.StateData = &roadmap_state_api.BasicStateInfo_TopicState{
			TopicState: &roadmap_state_api.NodeTopicState{
				State: roadmap_state_api.TOPIC_STATE_TYPE(item.BasicInfo.TopicState.State),
				Title: item.BasicInfo.TopicState.Title,
			},
		}
	} else if item.BasicInfo.TodoListState != nil {
		doneList := []*roadmap_state_api.NodeTodoStateItem{}
		for _, todoItem := range item.BasicInfo.TodoListState.DoneList {
			doneList = append(doneList, &roadmap_state_api.NodeTodoStateItem{
				TodoId: todoItem.TodoId,
				Title:  todoItem.Title,
			})
		}
		retItem.BasicInfo.StateData = &roadmap_state_api.BasicStateInfo_TodoListState{
			TodoListState: &roadmap_state_api.NodeTodoListState{
				DoneList: doneList,
			},
		}
	}
	return retItem
}

type _StateDao struct {
	mc *mongo.Collection
}

func newStateDao(db *mongo.Database) (*_StateDao, error) {
	mc := db.Collection(db_ns.DB_NS_ROADMAP + "state")
	_, err := mc.Indexes().CreateOne(context.Background(), mongo.IndexModel{
		Keys: bson.D{{"roadmapId", 1}, {"userId", 1}},
	})
	if err != nil {
		return nil, err
	}
	return &_StateDao{mc: mc}, nil
}

func (db *_StateDao) Upsert(ctx context.Context, dbItem *StateInfoDbItem) error {
	dbItem.ItemId = db.genItemId(dbItem.RoadmapId, dbItem.NodeId, dbItem.UserId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": dbItem.ItemId}, bson.M{"$set": dbItem},
		options.Update().SetUpsert(true))
	return err
}

func (db *_StateDao) genItemId(roadmapId, nodeId, userId string) string {
	return fmt.Sprintf("%s:%s:%s", roadmapId, nodeId, userId)
}

func (db *_StateDao) Exist(ctx context.Context, roadmapId, nodeId, userId string) (bool, error) {
	itemId := db.genItemId(roadmapId, nodeId, userId)
	count, err := db.mc.CountDocuments(ctx, bson.M{"_id": itemId})
	return count > 0, err
}

func (db *_StateDao) Get(ctx context.Context, roadmapId, nodeId, userId string) (*StateInfoDbItem, error) {
	itemId := db.genItemId(roadmapId, nodeId, userId)
	retItem := &StateInfoDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"_id": itemId}).Decode(retItem)
	return retItem, err
}

func (db *_StateDao) Remove(ctx context.Context, roadmapId, nodeId, userId string) error {
	itemId := db.genItemId(roadmapId, nodeId, userId)
	_, err := db.mc.DeleteOne(ctx, bson.M{"_id": itemId})
	return err
}

func (db *_StateDao) RemoveByNode(ctx context.Context, roadmapId, nodeId string) error {
	_, err := db.mc.DeleteMany(ctx, bson.M{"roadmapId": roadmapId, "nodeId": nodeId})
	return err
}

func (db *_StateDao) RemoveByRoadmap(ctx context.Context, roadmapId string) error {
	_, err := db.mc.DeleteMany(ctx, bson.M{"roadmapId": roadmapId})
	return err
}

func (db *_StateDao) List(ctx context.Context, roadmapId, userId string, filterByNodeId bool, nodeId string) ([]*StateInfoDbItem, error) {
	query := bson.M{"roadmapId": roadmapId, "userId": userId}
	if filterByNodeId {
		query["nodeId"] = nodeId
	}
	cursor, err := db.mc.Find(ctx, query)
	if err != nil {
		return nil, err
	}
	retList := []*StateInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}
